// Package middlewares
// Created by GoLand
// @User: lenora
// @Date: 2024/1/14
// @Time: 17:26

package middlewares

import (
	"bytes"
	"camp/microbook/internal/web"
	"camp/microbook/pkg/ginx"
	"context"
	"encoding/json"
	"github.com/gin-gonic/gin"
	"io"
)

type LogMiddlewareBuilder struct {
	logFn     func(ctx context.Context, al AccessLog)
	errFn     func(ctx context.Context, data string)
	allowReq  bool
	allowResp bool
}

func NewLogMiddlewareBuilder(logFn func(ctx context.Context, al AccessLog), errFn func(ctx context.Context, data string)) *LogMiddlewareBuilder {
	return &LogMiddlewareBuilder{
		logFn: logFn,
		errFn: errFn,
	}
}

func (l *LogMiddlewareBuilder) Build() gin.HandlerFunc {
	return func(c *gin.Context) {
		path := c.Request.URL.Path
		//拦截长路径
		if len(path) > 1024 {
			path = path[:1024]
		}
		method := c.Request.Method
		al := AccessLog{
			Path:   path,
			Method: method,
		}
		if l.allowReq {
			reqBody, err := c.GetRawData()
			if err != nil {
				//处理方式可自定义,可忽略  也可有其他造作
			}
			//Request.Body是一个Stream对象,只可读取一次,读取完之后需要重新放回去
			al.ReqBody = string(reqBody)
			c.Request.Body = io.NopCloser(bytes.NewReader(reqBody)) //也可使用bytes.NewBuffer(reqBody)
		}
		//gin的context没有暴露响应,可以通过已暴露的内容去写入信息
		c.Writer = &responseWriter{
			ResponseWriter: c.Writer,
			al:             &al,
		}
		var resp ginx.ApiResponse

		defer func() {
			//捕获next()之后发生的程序调用
			err := json.Unmarshal([]byte(al.RespBody), &resp)
			if err == nil && resp.Code != web.SUCCESS {
				l.errFn(c, al.RespBody)
			}
			if !l.allowResp {
				al.RespBody = ""
			}
			l.logFn(c, al)

		}()
		//执行下一个中间件
		c.Next()

		//所有中间件以及业务逻辑执行完之后
		//已经拿到了响应

	}
}

func (l *LogMiddlewareBuilder) AllowReqBody() *LogMiddlewareBuilder {
	l.allowReq = true
	return l
}

func (l *LogMiddlewareBuilder) AllowRespBody() *LogMiddlewareBuilder {
	l.allowResp = true
	return l
}

type AccessLog struct {
	Path     string `json:"path"`
	Method   string `json:"method"`
	ReqBody  string `json:"req_body"`
	Status   int    `json:"status"`
	RespBody string `json:"resp_body"`
}

type responseWriter struct {
	gin.ResponseWriter //gin内部记录响应的接口,引入并重写
	al                 *AccessLog
}

func (w *responseWriter) Write(data []byte) (int, error) {
	w.al.RespBody = string(data)
	return w.ResponseWriter.Write(data)
}

func (w *responseWriter) WriteHeader(statusCode int) {
	w.al.Status = statusCode
	w.ResponseWriter.WriteHeader(statusCode)
}
